﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using ActiveHomeScriptLib;
using System.Threading;

namespace _3DSensor
{
    public class X10GestureApp : AbstractGestureWindow, IGesturable
    {
        public MainWindow MainWindow { get; set; }
         ActiveHomeClass _x10Controller;
        public string Device { get; set; }
        public int CurrentBrightness { get; set; }

        public int TargetBrightness
        {
            get { return _targetBrightness; }
            set { _targetBrightness = value; }
        }

        private bool _isLampOn;
        private int _lastBrightnessValue;
        private DateTime _lastTimeChanged = DateTime.Now;
        internal const double Delta = 500;
        internal const double EPSILON = 5;

        private DateTime _lastGestureTime;
        private int _targetBrightness = 50;
        private string _currentCommand = "";

        public X10GestureApp(GestureRecogniser recogniser)
        {
            _lastGestureTime = new DateTime();
            InitializeComponent();
            recogniser.RegisterObservers(this);
            Device = "A2";
            //initiate the x10-Controller class
            _x10Controller = new ActiveHomeClass();
            _x10Controller.RecvAction += new _DIActiveHomeEvents_RecvActionEventHandler(OnActiveHomeRecv);

            #region Setting Up the System

            //setup pl
            //TestSendPLCPL16ON();
            while (!CheckLampState(false))
            {
                TurnLampOff();
            }


            if (CurrentBrightness != -1)
                Console.WriteLine(DateTime.Now.ToShortDateString() + @"Setup Successful");
            if (_isLampOn)
                TurnLampOff();


            #endregion
        }

        public X10GestureApp(GestureRecogniser recogniser,MainWindow mainWindow)
        {
            MainWindow = mainWindow;
            recogniser.RegisterObservers(this);
            Device = "A2";
            //initiate the x10-Controller class
            _x10Controller = new ActiveHomeClass();
            if (_isLampOn)
                TurnLampOff();
            InitializeComponent();
        }


        /// <summary>
        /// Response to the gesture provided by the GestureRecognizer 
        /// (This is a method that ensures the Observer pattern is upheld
        /// by the subscribing application, which includes this app as well)
        /// </summary>
        /// <param name="gesture">The gesture detected by the recogniser</param>
        /// <param name="epsilon"></param>
        public void ReceivedGesture(GestureBase gesture, double epsilon)
        {
            #region Process the Gesture and Set the NextBrightnaess

            //based on this gesture control the LAMP
            if (!_isLampOn && gesture.InnerGesture.Equals(Gesture.On))
            {
                NextTargetBrightness = 100;
            }
            else if (_isLampOn && gesture.InnerGesture.Equals(Gesture.Off))
            {
                NextTargetBrightness = 0;
            }
            else if (gesture.InnerGesture.Equals(Gesture.Close))
            {
                IsAppActive = false;
                return;
            }
            else if (gesture.InnerGesture.Equals(Gesture.Increase) || gesture.InnerGesture.Equals(Gesture.Decrease))
            {
                NextTargetBrightness = Convert.ToInt32(gesture.Value);
            }

            #endregion

            CheckLampState(false);
            if (!IsAppActive) return; //if not active then we return

            //send commands to the x10 interface every 6 seconds
            if ((DateTime.Now - _lastGestureTime).Seconds < 6)
            {
                return;
            }

            #region Check if the System is still busy or not

            if (!IsBrightnessChanging() || (CurrentBrightness == TargetBrightness)) //still pursuing target
            {
                IsBusy = false;
            }
            else if (IsBusy)
            {
               
            }
            
            #endregion

            #region Issue Brightness Control Commands when system is Not Busy

            if (IsBusy) return;
            TargetBrightness = NextTargetBrightness;
            IsBusy = true;
            _currentCommand = "-";

            //brighten the lamp
            if (CurrentBrightness < TargetBrightness)
            {
                BrightenLamp(TargetBrightness - CurrentBrightness);
            }
            // or dim the lamp
            else if (CurrentBrightness > TargetBrightness)
            {
                DimLamp(CurrentBrightness - TargetBrightness);
            }

            #endregion
        }


        /// <summary>
        /// tells us whether the brightness is changing or not
        /// </summary>
        /// <returns>bool</returns>
        private bool IsBrightnessChanging()
        {
            //check every DELTA milliseconds
            if ((DateTime.Now - _lastTimeChanged).Milliseconds >= Delta)
            {
                if (Math.Abs(CurrentBrightness - _lastBrightnessValue) == 0)
                {
                    _lastTimeChanged = DateTime.Now;
                    _lastBrightnessValue = CurrentBrightness;
                    return false;
                }
                _lastTimeChanged = DateTime.Now;
                _lastBrightnessValue = CurrentBrightness;
            }
            return true;
        }

        List<string> messages=new List<string>(10); 

        private void OnActiveHomeRecv(object strAction, object Address, object Command,
            object Reserved1, object Reserved2, object Reserved3, object Reserved4)
        {
            String strMsg = "";

            if (((String) strAction).ToUpper() == "RECVPLC")
            {}
            else
            {
                return;
            }

            if (((String)Address).ToLower().IndexOf(Device.ToLower())==-1)
            {
                return;
            }

            strMsg += " " + Address.ToString().ToUpper();
            strMsg += " " + Command.ToString().ToUpper();

            if (Reserved1.ToString().Length > 0)
                strMsg += " " + Reserved1.ToString().ToUpper();

            if (Reserved2.ToString().Length > 0)
                strMsg += " " + Reserved2.ToString().ToUpper();

            if (Reserved3.ToString().Length > 0)
                strMsg += " " + Reserved3.ToString().ToUpper();

            if (Reserved3 != null && Reserved3.ToString().Length > 0)
                strMsg += " " + Reserved3.ToString().ToUpper();


            StatusTextBox.AppendText(strMsg + "\r\n");
            if (IsBusy)
            {
                if (strMsg.ToLower().IndexOf(_currentCommand.ToLower()) != -1)
                {
                    IsBusy = false;
                }
                if (_currentCommand == "") IsBusy = false;
            }
            
            if((messages.IndexOf(strMsg))!=-1)
            {
                Thread.Sleep(1000);
            }
            else
            {
                messages.Add(strMsg);
            }

        }

        private DateTime _lastCheckTime = DateTime.Now;
        
        /// <summary>
        /// checks if the lamp is in an error state and then tries to fix the error state
        /// also checks if the lamp is on or not and sets the _isLampOn appropriately
        /// </summary>
        bool CheckLampState(bool delayNeeded)
        {
            if ((DateTime.Now - _lastCheckTime).Seconds > 1 || !delayNeeded)
            {
                if(_x10Controller==null) _x10Controller=new ActiveHomeClass();
                try
                {
                    CurrentBrightness = Convert.ToInt16(_x10Controller.SendAction("QueryPLC", Device + " Dim"));;
                    //1 denotes the lamp is on
                    _isLampOn = Convert.ToInt16(_x10Controller.SendAction("QueryPLC", Device + " On")).Equals(1);
                }
                catch (AccessViolationException e)
                {
                    throw e;
                }
                _lastCheckTime = DateTime.Now;
                //-1 denotes error state
                if (CurrentBrightness == -1)
                {
                    return false;
                }
                return true;
            }
            return true;
        }

        /// <summary>
        /// dims the lamp to a specific value expressed as a %
        /// </summary>
        /// <param name="dimByValue"></param>
        private void DimLamp(int dimByValue)
        {
            if (dimByValue >= 0 && dimByValue < 100)
            {
                if (CurrentBrightness - dimByValue < 0)
                    return;
                _x10Controller.SendAction("SendPLC", Device + " Dim " + dimByValue, null, null);
                _lastGestureTime = DateTime.Now;
                _currentCommand = Device + " Dim " + dimByValue;
            }
            Console.WriteLine(DateTime.Now.ToShortDateString() + "dim lamp " + dimByValue);
        }

        /// <summary>
        /// brightens the lamp to specific value expressed as a %
        /// </summary>
        /// <param name="brightenByValue"></param>
        private void BrightenLamp(int brightenByValue)
        {
            if (brightenByValue >= 5 && brightenByValue <= 100)
            {   
                if(CurrentBrightness+brightenByValue>100)
                    return;
                
                _x10Controller.SendAction("SendPLC", Device + " Bright " + brightenByValue, null, null);
                _lastGestureTime = DateTime.Now;
                _currentCommand = Device + " Bright " + brightenByValue;
            }
            Console.WriteLine(DateTime.Now.ToShortDateString() + "brighten lamp "+ brightenByValue);
        }


        /// <summary>
        /// turns the lamp on
        /// </summary>
        public void TurnLampOn()
        {
            //switch the lamp on
            _x10Controller.SendAction("SendPLC", Device + " On", null, null);
            _currentCommand = Device + " On";
            _isLampOn = true;
            _lastGestureTime = DateTime.Now;
            Console.WriteLine(DateTime.Now.ToShortDateString() + "turn lamp off");
        }
        
        /// <summary>
        /// turns the lamp off
        /// </summary>
        public void TurnLampOff()
        {
            //execute the actual action
            _x10Controller.SendAction("SendPLC", Device + " Off",null,null);
            _currentCommand = Device + " Off";
            _isLampOn = false;
            _lastGestureTime = DateTime.Now;
            Console.WriteLine(DateTime.Now.ToShortDateString() + "turn lamp off");
        }

        override protected void end_app()
        {
           Util.killProcess("X10nets.exe");
        }

        public int NextTargetBrightness { get; set; }
        
    }
}
